package com.tannuo.dolphinnotedemo.sdk.device.wifi;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.provider.Settings;
import android.util.Log;

import com.tannuo.dolphinnotedemo.sdk.device.ConnectionListener;
import com.tannuo.dolphinnotedemo.sdk.device.DeviceScreen;
import com.tannuo.dolphinnotedemo.sdk.device.IService;
import com.tannuo.dolphinnotedemo.sdk.device.MessageReceiveListener;
import com.tannuo.dolphinnotedemo.sdk.device.error.ConnectionErrorCode;
import com.tannuo.dolphinnotedemo.sdk.device.protocol.IMessage;
import com.tannuo.dolphinnotedemo.sdk.device.protocol.IProtocol;
import com.tannuo.dolphinnotedemo.sdk.device.protocol.ProtocolCode;
import com.tannuo.dolphinnotedemo.sdk.device.protocol.ZDMessage;
import com.tannuo.dolphinnotedemo.sdk.util.DataUtil;
import com.tannuo.dolphinnotedemo.sdk.util.Logger;
import com.tannuo.dolphinnotedemo.sdk.util.WifiUtil;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.Timer;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Created by hucn on 2016/7/29.
 * Description: 管理与设备的wifi连接
 */
public class WifiService implements IService {

    private final String TAG = WifiService.class.getSimpleName();

    private Context mContext;

    private MessageReceiveListener mReceiveListener;
    private WifiManager mWifiManager;
    private ConnectivityManager mConnectivityManager;

    private String UDP_CLIENT_IP="10.10.10.166"; // 本地IP地址

    private String UDP_DEVICE_IP; // 远程设备的IP地址

    private String UDP_MULTI_IP; // 广播的IP

    private int UDP_CLIENT_MULTI_PORT = 9400; // 本地IP地址

    private int UDP_CLIENT_PORT = 9401; // 本地IP地址

    private int UDP_DEVICE_MULTI_PORT = 9402; // 远程端口

    private int UDP_DEVICE_PORT = 9403; // 远程端口

    private DatagramSocket mUdpSocket;

    private DatagramSocket mMultiSocket;

    private String mDeviceId;

    private IProtocol mProtocol;

    private DeviceScreen mDeviceScreen;

    private MultiReceiveThread mMultiReceiveThread;

    private UdpSendThread mUdpSendThread;

    private UdpReceiveThread mUdpReceiveThread;

    private Timer connTimer; // 定时判断连接状态的计时器

    private int mMultiCount; // 目前的计时次数

    private int mConfirmCount; // 目前的计时次数

    private int mDisConnectedCount; // 断开连接的计时次数

    private static final int MAXMULTI = 10;

    private static final int MAXCONFIRM = 2;

    private static final int MAXDISCONN = 10;

    private ConnectionListener mConnectionListener;

    private final int VOICE_MAX_QUEUE_SIZE = 200;

    private LinkedBlockingQueue<byte[]> mSendQueue = new LinkedBlockingQueue<>(VOICE_MAX_QUEUE_SIZE);
    private LinkedBlockingQueue<byte[]> mMultiQueue = new LinkedBlockingQueue<>(VOICE_MAX_QUEUE_SIZE);

    private final int SUCCESS = 0;

    private final int ERROR = -1;

    public WifiService(Context context, IProtocol protocol, DeviceScreen deviceScreen, ConnectionListener connectionListener) {
        mContext = context;
        mProtocol = protocol;
        mDeviceScreen = deviceScreen;
        mConnectionListener = connectionListener;
    }

    /**
     * 初始化时检查网络状况，获取wifi相关信息
     *
     * @return int 错误信息，如果没有错误，返回SUCCESS
     */
    private int init() {
        initParams();
        if (checkWifiConnected()) {
            initWifiInfo();
            if (UDP_DEVICE_IP == null || ("0.0.0.0".equals(UDP_DEVICE_IP))) {
                Logger.e(TAG, "Wifi is enabled,but can not get the IP, please check the wifi settings");
                if (mConnectionListener != null) {
                    mConnectionListener.onError(new ConnectionErrorCode(ConnectionErrorCode.WIFI_ERROR_INIT_FAILED, "Wifi初始化错误，获取不到IP地址"));
                }
                return ERROR;
            }
            return SUCCESS;
        }
        Logger.e(TAG, "Wifi is not enable, please check the wifi settings");
        if (mConnectionListener != null) {
            mConnectionListener.onError(new ConnectionErrorCode(ConnectionErrorCode.WIFI_ERROR_INIT_FAILED, "Wifi初始化错误，请确认wifi已经打开"));
        }
        return ERROR;
    }

    /**
     * 初始化搜索引荐设备的socket
     *
     * @return int 错误信息，如果没有错误，返回SUCCESS
     */
    private int startMulti() {
        if (mMultiSocket != null) {
            mMultiSocket.close();
            mMultiSocket = null;
        }
        int errorCode = SUCCESS;
        try {
            mMultiSocket = new DatagramSocket(UDP_DEVICE_MULTI_PORT);
        } catch (IOException e) {
            if (mConnectionListener != null) {
                mConnectionListener.onError(new ConnectionErrorCode(ConnectionErrorCode.WIFI_ERROR_MULTI_FAILED, "UDP打开广播Socket出错,创建时出错"));
            }
            errorCode = ERROR;
            e.printStackTrace();
        }
        return errorCode;
    }

    private void initParams() {
        // WifiManager
        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
        // ConnectivityManager主要管理和网络连接相关的操作
        mConnectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        // 获得手机唯一识别码
        mDeviceId = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.ANDROID_ID);
    }

    /**
     * 初始化接收和发送消息的socket
     *
     * @return int 错误信息，如果没有错误，返回SUCCESS
     */
    private int createUdpSocket() {
        if (mUdpSocket != null) {
            mUdpSocket.close();
            mUdpSocket = null;
        }
        int errorCode = SUCCESS;
        try {
            mUdpSocket = new DatagramSocket(UDP_DEVICE_PORT);
        } catch (SocketException e) {
            if (mConnectionListener != null) {
                mConnectionListener.onError(new ConnectionErrorCode(ConnectionErrorCode.WIFI_ERROR_CONN_FAILED, "UDP打开连接Socket出错,创建时出错"));
            }
            errorCode = ERROR;
            e.printStackTrace();
        }
        return errorCode;
    }

    public int connect() {
        int errorCode = init();
        if (errorCode == SUCCESS) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int errorCode1 = startMulti();

                    if (errorCode1 == SUCCESS) {
                        beginSearching();
                    }

                    int errorCode2 = createUdpSocket();
                    if (errorCode2 == SUCCESS) {
                        if (mConnectionListener != null) {
                            mConnectionListener.onConnected();
                        }
                        mUdpSendThread = new UdpSendThread();
                        mUdpSendThread.start();
                        mUdpReceiveThread = new UdpReceiveThread();
                        mUdpReceiveThread.start();
                    }
                }
            }).start();
            /*if (connTimer != null) {
                connTimer.cancel();
                connTimer.purge();
                connTimer = null;
            }
            connTimer = new Timer("ConnectionTimer", false);
            connTimer.schedule(new ConnTimerTask(), 500, 1000);*/
        }
        return errorCode;
    }

    public void disconnect() {
        clearSocket();
        clearThread();
        if (connTimer != null) {
            connTimer.cancel();
            connTimer.purge();
            connTimer = null;
        }
    }

    /**
     * 开始搜索的接收和发送线程
     */
    private void beginSearching() {
        mMultiReceiveThread = new MultiReceiveThread();
        mMultiReceiveThread.start();
    }

    /**
     * 组网接收线程
     */
    private class MultiReceiveThread extends Thread {
        private boolean isStoped = false;

        @Override
        public void run() {
            Log.i(TAG, "Multi socket receive thread start.");
            this.setName(MultiReceiveThread.class.getName());

            while (!isStoped && !Thread.interrupted()) {
                if (mMultiSocket != null && mMultiSocket.isBound()) {
                    byte[] data = new byte[1024];
                    DatagramPacket packet = new DatagramPacket(data, data.length);
                    try {
                        mMultiSocket.receive(packet);
                        Logger.e(TAG, "Receive multi message =========================================================");
                        IMessage message = mProtocol.decode(packet.getData());
                        if(message.getFeature()!=null){
                            byte feature = (message.getFeature())[0];
                            if (feature != ProtocolCode.FUNC_REQUEST_CONNECTING) {
                                Logger.e(TAG, "Recive Wrong Multi Message's Feature");
                            }


                            String host = WifiUtil.convertIntIpAddress(WifiUtil.byte2Int(((InetSocketAddress) packet.
                                    getSocketAddress()).getAddress().getAddress()));
                            if (!host.equals(UDP_DEVICE_IP)) {
                                Logger.d(TAG, "Current Socket IP not equals the previous one");
                            } else {
                                Logger.d(TAG, "Current Socket IP equals the previous one");
                            }
                            UDP_CLIENT_IP = host;
                        }else{
                            Logger.e(TAG, "Feature is error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!11");
                        }

                        //ZDMessage response = new ZDMessage();
                        /*response.setId(new byte[]{0x11, 0x11});
                        response.setFeature(new byte[]{ProtocolCode.FUNC_RESPONSE_CONNECTING_SUCCESS});
                        response.setData(new byte[]{0x11,0x11});
                        byte[] responseBytes = response.getBytes();*/
                        ZDMessage msgBase = new ZDMessage();
                        msgBase.setId(new byte[]{0x11, 0x11});
                        msgBase.setFeature(new byte[]{0x76});
                        ByteBuffer buffer = ByteBuffer.allocate(12);
                        buffer.put(DataUtil.int2byteLen2Low(100), 0, 2);
                        buffer.put(DataUtil.int2byteLen2Low(100), 0, 2);
                        buffer.put(DataUtil.int2byteLen2Low(1), 0, 2);
                        buffer.put(DataUtil.int2byteLen2Low(1), 0, 2);
                        buffer.put(new byte[]{0}, 0, 1);
                        buffer.put(DataUtil.int2byteLen3Low(100), 0, 3);
                        msgBase.setData(buffer.array());
                        byte[] responseBytes = msgBase.getBytes();
                        DatagramPacket packet1 = new DatagramPacket(responseBytes, responseBytes.length);
                        //packet1.setSocketAddress(new InetSocketAddress(UDP_CLIENT_IP, UDP_CLIENT_MULTI_PORT));
                        packet1.setSocketAddress(packet.getSocketAddress());
                        if (mMultiSocket != null && mMultiSocket.isBound()) {
                            try {
                                mMultiSocket.send(packet1);
                                Logger.e(TAG, "Send udp message to device");
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } else {
                            Logger.e(TAG, "The socket is not bounded or is null, discard the message");
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                Logger.d(TAG, "send multi message to search data");
            }

        }

        public void finish() {
            isStoped = true;
        }
    }

    /**
     * 消息发送线程
     */
    private class UdpSendThread extends Thread {
        private boolean isStoped = false;

        @Override
        public void run() {
            this.setName(UdpSendThread.class.getName());
            Log.i(TAG, "Udp socket send thread start.");
            try {
                while (!isStoped && !Thread.interrupted()) {
                    byte[] data = mSendQueue.take();
                    DatagramPacket packet = new DatagramPacket(data, data.length);
                    if (UDP_CLIENT_IP != null) {
                        packet.setSocketAddress(new InetSocketAddress(UDP_CLIENT_IP, UDP_CLIENT_PORT));

                        if (mUdpSocket != null && mUdpSocket.isBound()) {
                            try {
                                Logger.d(TAG, "Send udp message to device");
                                mUdpSocket.send(packet);

                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } else {
                            Logger.e(TAG, "The socket is not bounded or is null, discard the message");
                        }
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void finish() {
            isStoped = true;
        }
    }

    /**
     * 消息接收线程
     */
    private class UdpReceiveThread extends Thread {
        private boolean isStoped = false;

        @Override
        public void run() {
            this.setName(UdpReceiveThread.class.getName());
            while (!isStoped && !Thread.interrupted()) {
                if (mUdpSocket != null && mUdpSocket.isBound()) {
                    byte[] data = new byte[1024 * 10];
                    DatagramPacket packet = new DatagramPacket(data, data.length);
                    try {
                        mUdpSocket.receive(packet);
                        Logger.e(TAG, "Receive Udp message");
                        if (mReceiveListener != null) {
                            mReceiveListener.onReceive(data);
                        }


                        ZDMessage msgBase = new ZDMessage();
                        msgBase.setId(new byte[]{0x11, 0x11});
                        msgBase.setFeature(new byte[]{0x76});
                        ByteBuffer buffer = ByteBuffer.allocate(12);
                        buffer.put(DataUtil.int2byteLen2Low(100), 0, 2);
                        buffer.put(DataUtil.int2byteLen2Low(100), 0, 2);
                        buffer.put(DataUtil.int2byteLen2Low(1), 0, 2);
                        buffer.put(DataUtil.int2byteLen2Low(1), 0, 2);
                        buffer.put(new byte[]{0}, 0, 1);
                        buffer.put(DataUtil.int2byteLen3Low(100), 0, 3);
                        msgBase.setData(buffer.array());
                        byte[] responseBytes = msgBase.getBytes();
                        DatagramPacket packet1 = new DatagramPacket(responseBytes, responseBytes.length);
                        //packet1.setSocketAddress(new InetSocketAddress(UDP_CLIENT_IP, UDP_CLIENT_PORT));
                        packet1.setSocketAddress(packet.getSocketAddress());
                        if (mMultiSocket != null && mMultiSocket.isBound()) {
                            try {
                                mMultiSocket.send(packet1);
                                Logger.e(TAG, "Send udp message to device");
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } else {
                            Logger.e(TAG, "The socket is not bounded or is null, discard the message");
                        }


                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
        }

        public void finish() {
            isStoped = true;
        }
    }

    /**
     * 关闭所有socket
     */
    private void clearSocket() {
        if (mMultiSocket != null) {
            mMultiSocket.close();
            mMultiSocket = null;
        }
        if (mUdpSocket != null) {
            mUdpSocket.close();
            mUdpSocket = null;
        }
    }

    /**
     * 关闭所有Thread
     */
    private void clearThread() {
        if (mMultiReceiveThread != null) {
            mMultiReceiveThread.finish();
            mMultiReceiveThread = null;
        }
        if (mUdpSendThread != null) {
            mUdpSendThread.finish();
            mUdpSendThread = null;
        }
        if (mUdpReceiveThread != null) {
            mUdpReceiveThread.finish();
            mUdpReceiveThread = null;
        }
    }

    private void closeUdpAndThread() {
        if (mUdpSocket != null) {
            mUdpSocket.close();
            mUdpSocket = null;
        }
        if (mUdpSendThread != null) {
            mUdpSendThread.finish();
            mUdpSendThread = null;
        }
        if (mUdpReceiveThread != null) {
            mUdpReceiveThread.finish();
            mUdpReceiveThread = null;
        }
    }

    private void initWifiInfo() {
        // 192.168.1.100/24
        UDP_DEVICE_IP = WifiUtil.convertIntIpAddress(mWifiManager.getConnectionInfo().getIpAddress());
        if (UDP_DEVICE_IP != null) {
            String[] ipArray = UDP_DEVICE_IP.trim().split("\\.");
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < 3; i++) {
                builder.append(ipArray[i]);
                builder.append(".");
            }
            builder.append("255");
            UDP_MULTI_IP = builder.toString();
        }
    }

    /**
     * 判断wifi是否连接
     */
    private boolean checkWifiConnected() {
        NetworkInfo wifiNetworkInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        return wifiNetworkInfo.isConnected();
    }

    /**
     * 接收到正确信息的调用，表示设备保持连接
     */
    @Override
    public void beatAlive() {
        mDisConnectedCount = 0;
        mConfirmCount = 0;
        mMultiCount = 0;
    }

    @Override
    public void setOnReceiverListener(MessageReceiveListener receiveListener) {

    }

    @Override
    public void write(byte[] bytes) {
        try {
            mSendQueue.put(bytes);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取广播包的内容
     */
    private byte[] getMultiMessageContent() {
        ByteBuffer byteBuffer = ByteBuffer.allocate(28);
        //// TODO: 2016/8/5 设置发送广播消息的格式
        byteBuffer.put(new byte[4]);
        byteBuffer.put(new byte[16]);
        byteBuffer.put(new byte[8]);
        return byteBuffer.array();
    }

    private DatagramPacket getMultiPacket() {
        IMessage message = new ZDMessage();
        message.setId(WifiService.this.getId());
        message.setFeature(ProtocolCode.FUNC_REQUEST_CONNECTING);
        message.setData(getMultiMessageContent());
        byte[] data = mProtocol.encode(message);
        Logger.e(TAG, "Multi Socket get " + data.length + " size message to send");
        DatagramPacket packet = new DatagramPacket(data, data.length);
        packet.setSocketAddress(new InetSocketAddress(UDP_MULTI_IP, UDP_DEVICE_MULTI_PORT));
        return packet;
    }

    // TODO: 2016/8/5 发送的信息
    private void SendConfirmMessage() {

    }

    // TODO: 2016/8/5 确认发送的id
    private byte[] getId() {
        return new byte[]{0x11, 0x11};
    }
}
